#!/usr/bin/env python3
"""
AIQuant Requirements-dev.txt 最终验证脚本
验证修复后的 requirements-dev.txt 文件的质量和完整性
"""

import re
import sys
from pathlib import Path
from typing import Dict, List, Set, Tuple, Optional
from collections import defaultdict
import subprocess
import json

class RequirementsValidator:
    """Requirements-dev.txt 验证器"""
    
    def __init__(self, requirements_dev_path: str, requirements_path: str):
        self.requirements_dev_path = Path(requirements_dev_path)
        self.requirements_path = Path(requirements_path)
        self.validation_results = {
            'syntax': {'passed': True, 'issues': []},
            'duplicates': {'passed': True, 'issues': []},
            'version_compatibility': {'passed': True, 'issues': []},
            'security': {'passed': True, 'issues': []},
            'completeness': {'passed': True, 'issues': []},
            'best_practices': {'passed': True, 'issues': []},
            'functionality': {'passed': True, 'issues': []}
        }
        
    def run_validation(self) -> Dict:
        """运行所有验证检查"""
        print("🔍 开始 requirements-dev.txt 最终验证...")
        
        # 1. 语法验证
        print("\n1️⃣ 语法验证...")
        self._validate_syntax()
        
        # 2. 重复依赖检查
        print("\n2️⃣ 重复依赖检查...")
        self._check_duplicates()
        
        # 3. 版本兼容性验证
        print("\n3️⃣ 版本兼容性验证...")
        self._validate_version_compatibility()
        
        # 4. 安全性验证
        print("\n4️⃣ 安全性验证...")
        self._validate_security()
        
        # 5. 完整性检查
        print("\n5️⃣ 完整性检查...")
        self._check_completeness()
        
        # 6. 最佳实践验证
        print("\n6️⃣ 最佳实践验证...")
        self._validate_best_practices()
        
        # 7. 功能性验证
        print("\n7️⃣ 功能性验证...")
        self._validate_functionality()
        
        return self.validation_results
    
    def _validate_syntax(self):
        """验证文件语法"""
        try:
            with open(self.requirements_dev_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            
            for line_num, line in enumerate(lines, 1):
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                
                # 检查版本约束格式
                if not self._is_valid_requirement_line(line):
                    self.validation_results['syntax']['passed'] = False
                    self.validation_results['syntax']['issues'].append(
                        f"第 {line_num} 行语法错误: {line}"
                    )
                
                # 检查平台特定依赖语法
                if ';' in line:
                    if not self._is_valid_platform_condition(line):
                        self.validation_results['syntax']['passed'] = False
                        self.validation_results['syntax']['issues'].append(
                            f"第 {line_num} 行平台条件语法错误: {line}"
                        )
            
            if self.validation_results['syntax']['passed']:
                print("   ✅ 语法验证通过")
            else:
                print(f"   ❌ 发现 {len(self.validation_results['syntax']['issues'])} 个语法问题")
                
        except Exception as e:
            self.validation_results['syntax']['passed'] = False
            self.validation_results['syntax']['issues'].append(f"文件读取错误: {str(e)}")
    
    def _is_valid_requirement_line(self, line: str) -> bool:
        """检查依赖行格式是否正确"""
        # 添加调试日志
        print(f"   🔍 验证行: {line}")
        
        # 更宽松的正则表达式，支持更多格式
        # 支持: package_name[extras]>=version,<version; condition
        # 支持特殊字符如 ++, -, _
        pattern = r'^[a-zA-Z0-9_+-]+(\[[a-zA-Z0-9_,.-]+\])?(([><=!~]+[0-9.]+[a-zA-Z0-9]*[,\s]*)*)?(\s*;\s*.+)?$'
        is_valid = bool(re.match(pattern, line))
        
        if not is_valid:
            print(f"   ❌ 不匹配模式: {pattern}")
        else:
            print(f"   ✅ 匹配成功")
            
        return is_valid
    
    def _is_valid_platform_condition(self, line: str) -> bool:
        """检查平台条件语法"""
        if ';' not in line:
            return True
        
        condition_part = line.split(';', 1)[1].strip()
        valid_conditions = [
            'sys_platform == "win32"',
            'sys_platform == "darwin"', 
            'sys_platform == "linux"',
            'extra == "deep-learning"',
            'extra == "hpc"',
            'extra == "blockchain"'
        ]
        
        return any(cond in condition_part for cond in valid_conditions)
    
    def _check_duplicates(self):
        """检查重复依赖"""
        packages = defaultdict(list)
        
        try:
            with open(self.requirements_dev_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            
            for line_num, line in enumerate(lines, 1):
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                
                # 提取包名
                package_name = self._extract_package_name(line)
                if package_name:
                    packages[package_name].append(line_num)
            
            # 检查重复
            duplicates = {pkg: lines for pkg, lines in packages.items() if len(lines) > 1}
            
            if duplicates:
                self.validation_results['duplicates']['passed'] = False
                for pkg, lines in duplicates.items():
                    self.validation_results['duplicates']['issues'].append(
                        f"包 '{pkg}' 在第 {', '.join(map(str, lines))} 行重复定义"
                    )
                print(f"   ❌ 发现 {len(duplicates)} 个重复依赖")
            else:
                print("   ✅ 无重复依赖")
                
        except Exception as e:
            self.validation_results['duplicates']['passed'] = False
            self.validation_results['duplicates']['issues'].append(f"重复检查错误: {str(e)}")
    
    def _extract_package_name(self, line: str) -> Optional[str]:
        """从依赖行提取包名"""
        # 移除条件部分
        if ';' in line:
            line = line.split(';')[0].strip()
        
        # 提取包名（移除版本约束和extras）
        match = re.match(r'^([a-zA-Z0-9_-]+)', line)
        return match.group(1) if match else None
    
    def _validate_version_compatibility(self):
        """验证版本兼容性"""
        dev_packages = self._parse_requirements(self.requirements_dev_path)
        prod_packages = self._parse_requirements(self.requirements_path)
        
        conflicts = []
        
        for pkg_name, dev_version in dev_packages.items():
            if pkg_name in prod_packages:
                prod_version = prod_packages[pkg_name]
                if not self._are_versions_compatible(dev_version, prod_version):
                    conflicts.append(f"包 '{pkg_name}': dev({dev_version}) vs prod({prod_version})")
        
        if conflicts:
            self.validation_results['version_compatibility']['passed'] = False
            self.validation_results['version_compatibility']['issues'] = conflicts
            print(f"   ❌ 发现 {len(conflicts)} 个版本冲突")
        else:
            print("   ✅ 版本兼容性检查通过")
    
    def _parse_requirements(self, file_path: Path) -> Dict[str, str]:
        """解析 requirements 文件"""
        packages = {}
        
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
                    
                    # 跳过平台特定依赖
                    if ';' in line:
                        continue
                    
                    package_name = self._extract_package_name(line)
                    if package_name:
                        packages[package_name] = line
                        
        except Exception as e:
            print(f"   ⚠️ 解析文件 {file_path} 时出错: {e}")
            
        return packages
    
    def _are_versions_compatible(self, dev_version: str, prod_version: str) -> bool:
        """检查版本是否兼容"""
        # 简化的兼容性检查
        # 实际应该使用 packaging 库进行更精确的版本比较
        dev_pkg = self._extract_package_name(dev_version)
        prod_pkg = self._extract_package_name(prod_version)
        
        return dev_pkg == prod_pkg  # 基本检查包名一致
    
    def _validate_security(self):
        """验证安全性"""
        security_packages = {
            'cryptography': '>=41.0.0',  # 修复多个 CVE
            'requests': '>=2.31.0',      # 安全更新
            'urllib3': '>=2.0.0',        # 安全更新
        }
        
        try:
            with open(self.requirements_dev_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            for pkg, min_version in security_packages.items():
                if pkg in content:
                    # 检查是否满足最低安全版本
                    pattern = rf'{pkg}[>=<\s]*([0-9.]+)'
                    match = re.search(pattern, content)
                    if match:
                        version = match.group(1)
                        # 简化版本比较
                        if not self._version_meets_minimum(version, min_version.replace('>=', '')):
                            self.validation_results['security']['passed'] = False
                            self.validation_results['security']['issues'].append(
                                f"包 '{pkg}' 版本 {version} 低于安全要求 {min_version}"
                            )
            
            if self.validation_results['security']['passed']:
                print("   ✅ 安全性验证通过")
            else:
                print(f"   ❌ 发现 {len(self.validation_results['security']['issues'])} 个安全问题")
                
        except Exception as e:
            self.validation_results['security']['passed'] = False
            self.validation_results['security']['issues'].append(f"安全检查错误: {str(e)}")
    
    def _version_meets_minimum(self, current: str, minimum: str) -> bool:
        """检查版本是否满足最低要求"""
        try:
            current_parts = [int(x) for x in current.split('.')]
            minimum_parts = [int(x) for x in minimum.split('.')]
            
            # 补齐长度
            max_len = max(len(current_parts), len(minimum_parts))
            current_parts.extend([0] * (max_len - len(current_parts)))
            minimum_parts.extend([0] * (max_len - len(minimum_parts)))
            
            return current_parts >= minimum_parts
        except:
            return True  # 无法比较时假设满足
    
    def _check_completeness(self):
        """检查完整性"""
        required_categories = {
            '测试框架': ['pytest', 'pytest-asyncio', 'pytest-cov'],
            '代码质量': ['black', 'flake8', 'mypy', 'isort'],
            '安全分析': ['bandit', 'safety'],
            '调试工具': ['ipdb', 'py-spy'],
            '文档生成': ['sphinx', 'mkdocs'],
            '开发服务器': ['uvicorn', 'gunicorn'],
            '数据科学': ['jupyter', 'pandas', 'numpy', 'matplotlib'],
            '机器学习': ['scikit-learn', 'xgboost', 'lightgbm'],
            '金融数据': ['yfinance', 'akshare', 'tushare']
        }
        
        try:
            with open(self.requirements_dev_path, 'r', encoding='utf-8') as f:
                content = f.read().lower()
            
            missing_packages = []
            
            for category, packages in required_categories.items():
                missing_in_category = []
                for pkg in packages:
                    if pkg.lower() not in content:
                        missing_in_category.append(pkg)
                
                if missing_in_category:
                    missing_packages.append(f"{category}: {', '.join(missing_in_category)}")
            
            if missing_packages:
                self.validation_results['completeness']['passed'] = False
                self.validation_results['completeness']['issues'] = missing_packages
                print(f"   ❌ 发现 {len(missing_packages)} 个类别缺少必要包")
            else:
                print("   ✅ 完整性检查通过")
                
        except Exception as e:
            self.validation_results['completeness']['passed'] = False
            self.validation_results['completeness']['issues'].append(f"完整性检查错误: {str(e)}")
    
    def _validate_best_practices(self):
        """验证最佳实践"""
        issues = []
        
        try:
            with open(self.requirements_dev_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            
            # 检查版本约束策略
            version_patterns = {
                'exact': 0,      # ==
                'compatible': 0,  # >=x.y.z,<major+1.0.0
                'loose': 0       # >= only
            }
            
            for line in lines:
                line = line.strip()
                if not line or line.startswith('#'):
                    continue
                
                if '==' in line:
                    version_patterns['exact'] += 1
                elif '>=' in line and '<' in line:
                    version_patterns['compatible'] += 1
                elif '>=' in line:
                    version_patterns['loose'] += 1
            
            total_packages = sum(version_patterns.values())
            if total_packages > 0:
                compatible_ratio = version_patterns['compatible'] / total_packages
                if compatible_ratio < 0.8:  # 80% 应该使用兼容版本范围
                    issues.append(f"兼容版本约束比例过低: {compatible_ratio:.1%} (建议 >80%)")
            
            # 检查注释和文档
            comment_lines = sum(1 for line in lines if line.strip().startswith('#'))
            total_lines = len([line for line in lines if line.strip()])
            
            if total_lines > 0:
                comment_ratio = comment_lines / total_lines
                if comment_ratio < 0.3:  # 30% 应该是注释
                    issues.append(f"注释比例过低: {comment_ratio:.1%} (建议 >30%)")
            
            if issues:
                self.validation_results['best_practices']['passed'] = False
                self.validation_results['best_practices']['issues'] = issues
                print(f"   ❌ 发现 {len(issues)} 个最佳实践问题")
            else:
                print("   ✅ 最佳实践验证通过")
                
        except Exception as e:
            self.validation_results['best_practices']['passed'] = False
            self.validation_results['best_practices']['issues'].append(f"最佳实践检查错误: {str(e)}")
    
    def _validate_functionality(self):
        """验证功能性"""
        # 检查是否可以被 pip 解析
        try:
            # 添加调试日志
            print(f"   🔍 执行 pip 解析验证命令...")
            cmd = ['python', '-m', 'pip', 'install', '--dry-run', '-r', str(self.requirements_dev_path)]
            print(f"   🔍 命令: {' '.join(cmd)}")
            
            # 尝试使用 pip-tools 验证
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=30
            )
            
            # 添加详细的调试输出
            print(f"   🔍 返回码: {result.returncode}")
            if result.stdout:
                print(f"   🔍 标准输出: {result.stdout[:500]}...")  # 限制输出长度
            if result.stderr:
                print(f"   🔍 错误输出: {result.stderr[:500]}...")  # 限制输出长度
            
            if result.returncode != 0:
                self.validation_results['functionality']['passed'] = False
                self.validation_results['functionality']['issues'].append(
                    f"pip 解析失败: {result.stderr}"
                )
                print("   ❌ pip 解析验证失败")
                
                # 分析具体的错误原因
                if "pdb++" in result.stderr:
                    print("   🔍 检测到 pdb++ 包名解析问题")
                if "Invalid requirement" in result.stderr:
                    print("   🔍 检测到无效的依赖格式")
            else:
                print("   ✅ pip 解析验证通过")
                
        except subprocess.TimeoutExpired:
            self.validation_results['functionality']['issues'].append("pip 验证超时")
            print("   ⚠️ pip 验证超时")
        except FileNotFoundError:
            self.validation_results['functionality']['issues'].append("pip 命令未找到")
            print("   ⚠️ pip 命令未找到，跳过功能性验证")
        except Exception as e:
            self.validation_results['functionality']['issues'].append(f"功能性验证错误: {str(e)}")
            print(f"   ⚠️ 功能性验证错误: {e}")
    
    def generate_report(self) -> str:
        """生成验证报告"""
        report = []
        report.append("=" * 80)
        report.append("AIQuant Requirements-dev.txt 最终验证报告")
        report.append("=" * 80)
        report.append(f"验证时间: {self._get_current_time()}")
        report.append(f"文件路径: {self.requirements_dev_path}")
        report.append("")
        
        # 总体评分
        total_checks = len(self.validation_results)
        passed_checks = sum(1 for result in self.validation_results.values() if result['passed'])
        score = (passed_checks / total_checks) * 100
        
        report.append(f"🎯 总体质量评分: {score:.1f}% ({passed_checks}/{total_checks} 项通过)")
        report.append("")
        
        # 详细结果
        for check_name, result in self.validation_results.items():
            status = "✅ 通过" if result['passed'] else "❌ 失败"
            report.append(f"{self._get_check_emoji(check_name)} {self._get_check_title(check_name)}: {status}")
            
            if result['issues']:
                for issue in result['issues']:
                    report.append(f"   • {issue}")
                report.append("")
        
        # 修复质量评估
        report.append("📊 修复质量评估:")
        if score >= 90:
            report.append("   🌟 优秀 - 修复质量极高，可以投入生产使用")
        elif score >= 80:
            report.append("   ✅ 良好 - 修复质量较高，建议解决剩余问题后使用")
        elif score >= 70:
            report.append("   ⚠️ 一般 - 修复有效但仍有重要问题需要解决")
        else:
            report.append("   ❌ 需要改进 - 存在严重问题，需要进一步修复")
        
        report.append("")
        report.append("🔍 建议:")
        if score < 100:
            report.append("   • 解决上述发现的问题")
            report.append("   • 重新运行验证确保修复效果")
        else:
            report.append("   • 所有验证项目均已通过")
            report.append("   • 文件质量优秀，可以安全使用")
        
        return "\n".join(report)
    
    def _get_check_emoji(self, check_name: str) -> str:
        """获取检查项目的表情符号"""
        emojis = {
            'syntax': '📝',
            'duplicates': '🔄',
            'version_compatibility': '🔗',
            'security': '🔒',
            'completeness': '📋',
            'best_practices': '⭐',
            'functionality': '⚙️'
        }
        return emojis.get(check_name, '🔍')
    
    def _get_check_title(self, check_name: str) -> str:
        """获取检查项目的标题"""
        titles = {
            'syntax': '语法验证',
            'duplicates': '重复依赖检查',
            'version_compatibility': '版本兼容性验证',
            'security': '安全性验证',
            'completeness': '完整性检查',
            'best_practices': '最佳实践验证',
            'functionality': '功能性验证'
        }
        return titles.get(check_name, check_name)
    
    def _get_current_time(self) -> str:
        """获取当前时间"""
        from datetime import datetime
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def main():
    """主函数"""
    # 文件路径
    requirements_dev_path = "requirements-dev.txt"
    requirements_path = "requirements.txt"
    
    # 创建验证器
    validator = RequirementsValidator(requirements_dev_path, requirements_path)
    
    # 运行验证
    results = validator.run_validation()
    
    # 生成报告
    report = validator.generate_report()
    print("\n" + report)
    
    # 保存报告到文件
    report_file = "requirements_dev_validation_report.txt"
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write(report)
    
    print(f"\n📄 详细报告已保存到: {report_file}")
    
    # 返回退出码
    all_passed = all(result['passed'] for result in results.values())
    return 0 if all_passed else 1


if __name__ == "__main__":
    sys.exit(main())